use anyhow::anyhow;
use anyhow::Result;
use eastmoney::index::indexes_sz::indexes_sz;
use eastmoney::stock::stocks_board::StockBoard;
use eastmoney::stock::stocks_board::StockBoardData;
use fudata::db;
use fudata::model::security;
use fudata::model::FudaRecord;
use log::debug;
use log::error;
use log::info;
use log::trace;

/// 深证系列指数
///
/// pn: page-number start from 1,
///
/// pz: page-size e.g. 20
///
pub async fn update_indexes_sz_list_page_from_eastmoney(
    pn: u16,
    pz: u16
) -> Result<Vec<FudaRecord>> {
    let mut updates = Vec::new();
    let stock_board = indexes_sz(pn, pz).await.map_err(|err| anyhow!(err.to_string()))?;
    if let StockBoard { data: Some(StockBoardData { diff, .. }), .. } = stock_board {
        for (_idx, df) in diff.iter().enumerate() {
            let code = df.f12.trim();
            let short_name = df.f14.trim();
            if !code.is_empty() && !short_name.is_empty() {
                let mkt = security::Market::SZ;
                trace!("{mkt}.{}.{}, {}", security::SecurityType::IDX, code, short_name);

                let security = security::Security {
                    market: mkt,
                    type_: security::SecurityType::IDX,
                    symbol: code.to_owned(),
                    name: Some(short_name.to_owned()),
                    ..Default::default()
                };

                // Update a security record with a specific id
                let updated: Option<FudaRecord> = db::security::upsert(&security).await?;

                if let Some(u) = updated {
                    updates.push(u);
                }
            }
        }
    }
    Ok(updates)
}

pub async fn update_indexes_sz_list_all_from_eastmoney() -> Result<()> {
    let mut pn = 1;
    let pz = 1000;

    loop {
        info!(
            "(stocks_craw::update_indexes_sz_list_all_from_eastmoney) start! for page: {pn} page-size: {pz}"
        );
        let rs = update_indexes_sz_list_page_from_eastmoney(pn, pz).await;
        match rs {
            Ok(records) => {
                if records.is_empty() {
                    info!("debug No more records available; exit the loop");

                    break; // No more records available; exit the loop
                }
                pn += 1; // Increment the page number for the next request
                debug!("..{}", pn);
            }
            Err(error) => {
                // 错误处理: 打印错误日志并采取相应措施
                error!("Update error on page {}: {:?}", pn, error);

                return Err(error); // An error occurred; exit the loop
            }
        }
        info!(
            "(stocks_craw::update_indexes_sz_list_all_from_eastmoney) /end! for page: {pn} page-size: {pz}"
        );
    }

    Ok(())
}

#[cfg(test)]
mod test {
    use fudata::{ db, model::security };
    use serde::Deserialize;

    use crate::stock::stocks_craw;

    #[derive(Debug, Deserialize, PartialEq)]
    struct Count {
        count: u64,
    }

    #[tokio::test]
    async fn test_update_indexes_sz_list_page_from_eastmoney() {
        let _ = fudata::db::init().await.unwrap();

        let mut pn = 1;
        let pz = 1000;
        while pn < 5 {
            let rs = super::update_indexes_sz_list_page_from_eastmoney(pn, pz).await;
            match rs {
                Ok(records) => {
                    if records.is_empty() {
                        break; // No more records available; exit the loop
                    }
                    pn += 1; // Increment the page number for the next request
                    println!("..{}", pn);
                }
                Err(error) => {
                    // 错误处理: 打印错误日志并采取相应措施
                    println!("Update error on page {}: {:?}", pn, error);
                    break; // An error occurred; exit the loop
                }
            }
        }

        let rs = db::security::select(
            &Some(security::Market::SZ),
            Some(fudata::model::security::SecurityType::IDX),
            None,
            Some("id ASC"),
            0,
            1000
        ).await;
        match rs {
            Ok(records) => {
                for (idx, v) in records.iter().enumerate() {
                    if idx < 10 || idx > records.len() - 10 {
                        println!("[{:00004}] {:?},{:?} ", idx, v.id, v.name);
                    } else {
                        print!(".");
                    }
                }
            }
            Err(error) => {
                println!("{:?}", error);
            }
        }
    }
}
